import { Propagation } from "./Propagation"
import { Isolation } from "./Isolation"
import { SQLException } from "./SQLException"
import { TransactionContext } from "./TransactionContext"

export default function wrapInTransaction(
  fn, propagation: Propagation, isolation: Isolation
) {
  // 这个this 指的是 fn 所在的 类 实例化后的对象
  async function wrapped(this, ...args: unknown[]) {

    const transactionContext: TransactionContext = TransactionContext.getInstance()
    const runOriginal = async () => {
      // console.log(`runOriginal `, fn.name)
      return fn.apply(this, [...args])
    }
    const runWithNewTransaction = async () => {
      // console.log(`runWithNewTransaction `, fn.name)
      const transactionCallback = async () => {
        const transactionInfo = transactionContext.getConnectionInfo()
        const { connection } = transactionInfo
        try {
          // TODO 需要这个吗
          // await connection.query('SET AUTOCOMMIT = 0')
          // query('START TRANSACTION')
          if (isolation != Isolation.DEFAULT) {
            await connection.query(`SET TRANSACTION ISOLATION LEVEL ${isolation}`)
          }
          // MySQL <==> await connection.query('START TRANSACTION')
          await connection.beginTransaction()
          // console.log(`Transaction begin`, fn.name)
          const result = await fn.apply(this, [...args])
          // MySQL <==> await connection.query('COMMIT')
          await connection.commit()
          // console.log(`Transaction commit`, fn.name)
          return result
        } catch (e) {
          if (connection) {
            try {
              // MySQL <==> await connection.query('ROLLBACK');
              await connection.rollback()
            } catch (e) {
            }
          }
          throw e
        } finally {
          if (connection) {
            // 放回连接池
            connection.release()
          }
        }
      }

      return await transactionContext.runInNewHookContext(transactionCallback)
    }

    const transactionInfo = transactionContext.getConnectionInfo()
    // console.log(`wrapped propagation = `, propagation)
    switch (propagation) {
      case Propagation.MANDATORY:
        if (transactionInfo == null) {
          throw new SQLException(
            "No existing transaction found for transaction marked with propagation 'MANDATORY'"
          )
        }
        return await runOriginal()
      case Propagation.NESTED:
        return await runWithNewTransaction()
      case Propagation.NEVER:
        return await runOriginal()
      case Propagation.NOT_SUPPORTED:
        return await runOriginal()
      case Propagation.REQUIRED:
        if (transactionInfo == null) {
          return await runWithNewTransaction()
        }
        return await runOriginal()
      case Propagation.REQUIRES_NEW:
        return await runWithNewTransaction()
      case Propagation.SUPPORTS:
        return await runOriginal()
    }
  }

  return wrapped
}
